#include "../include/SoundEngine.hpp"
#include "../include/Utils.hpp"
using namespace klas;

bool SoundEngine::m_needToInit = true;
bool SoundEngine::m_playingTracklist = false;
vector<Music*> SoundEngine::m_trackList;
vector<Music*>::const_iterator SoundEngine::m_playingTrack;

SoundEngine::SoundEngine(iReal targetNumOfTracks, iReal frequency, iReal channels, iReal chunkSize, Uint16 format)
{
  if(m_needToInit)
    if(Mix_OpenAudio(frequency, format, channels, chunkSize) == -1)
    {
      cout << "Mix_OpenAudio error: " << Mix_GetError() << endl;
      Utils::log("Mix_OpenAudio failed.");
    }

  Mix_QuerySpec(&m_frequency, &m_format, &m_channels);
  m_chunkSize = chunkSize;
  m_trackList.reserve(targetNumOfTracks);
  m_playingTrack = m_trackList.begin();
  m_playingTracklist = false;


  Mix_HookMusicFinished(&SoundEngine::runTracklistCallback);
}

SoundEngine::~SoundEngine()
{
  if(m_needToInit == false)
  {
    Mix_CloseAudio();
    m_needToInit = true;
  }
}

bool SoundEngine::playEffect(DataObject* effect, iReal loops)
{
  if(effect->getObjectType() == Data::SOUND_EFFECT)
  {
    if(Mix_PlayChannel(-1, dynamic_cast<SoundEffect*>(effect)->getEffect(), loops) == -1)
    {
      cout << "Mix_PlayChannel Error: " << Mix_GetError() << endl;
      Utils::log("SoundEngine::playEffect failed to play the effect.");
      return false;
    }

    return true;
  }

  return false;
}

bool SoundEngine::playMusic(DataObject* music, iReal loops, Real startSeconds, iReal fadeInTimeMS)
{
  if(music->getObjectType() == Data::MUSIC)
  {
    if(Mix_PlayMusic(dynamic_cast<Music*>(music)->getMusic(), loops) == -1)
    {
      cout << "Mix_PlayMusic Error: " << Mix_GetError() << endl;
      Utils::log("SoundEngine::playMusic failed to play the music.");
      return false;
    }

    if(startSeconds > 0)
    {
      if(Mix_FadeInMusicPos(dynamic_cast<Music*>(music)->getMusic(), loops, fadeInTimeMS, startSeconds) == -1)
      {
        cout << "Mix_FadeInMusic Error: " << Mix_GetError() << endl;
        Utils::log("SoundEngine::playMusic failed to change the time of the playing music.");
        return false;
      }
    }

    return true;
  }

  cout << "bad" << endl;
  return false;
}


bool SoundEngine::addTrack(DataObject* music)
{
  if(music->getObjectType() == Data::MUSIC)
  {
    m_trackList.push_back(dynamic_cast<Music*>(music));
    return true;
  }

  return false;
}

void SoundEngine::playTracklist()
{
  m_playingTracklist = true;
  runTracklistCallback();
}

void SoundEngine::skipTrack()
{
  runTracklistCallback();
}

void SoundEngine::previousTrack()
{
  if(m_playingTracklist)
  {
    --m_playingTrack;
    if(m_playingTrack == m_trackList.begin())
      m_playingTrack = m_trackList.end();

    if(Mix_PlayMusic((*m_playingTrack)->getMusic(), 0) == -1)
    {
      cout << "From endOfTrackCallback() Mix_PlayMusic error: " << Mix_GetError() << endl;
      Utils::log("SoundEngine::playMusic failed to change the time of the playing music.");
    }
  }
}

bool SoundEngine::stopPlayingMuisc()
{
  m_playingTracklist = false;
  if(Mix_HaltMusic() == -1)
  {
    cout << "Mix_HaltMusic() error: " << Mix_GetError() << endl;
    Utils::log("SoundEngine::stopPlayingMusic failed to stop playing the music.");
    return false;
  }

  return true;
}

bool SoundEngine::pauseMusic()
{
  Mix_PauseMusic();
}

bool SoundEngine::resumeMusic()
{
  Mix_ResumeMusic();
}

void SoundEngine::runTracklistCallback()
{
  if(m_playingTracklist)
  {
    ++m_playingTrack;
    if(m_playingTrack == m_trackList.end())
      m_playingTrack = m_trackList.begin();

    if(Mix_PlayMusic((*m_playingTrack)->getMusic(), 0) == -1)
    {
      cout << "From endOfTrackCallback() Mix_PlayMusic error: " << Mix_GetError() << endl;
      Utils::log("SoundEngine::playMusic failed to change the time of the playing music.");
    }
  }
}




/* Music implemenation */

Music::Music(string file)
{
  m_music = Mix_LoadMUS(file.c_str());
  if(m_music != NULL)
    m_isValid = true;
  else
  {
    m_isValid = false;
    cout << "Mix_LoadMUS error: " << Mix_GetError() << endl;
    Utils::log("Music::Music error loading music.");
  }
}

Music::~Music()
{
}

bool Music::isValid() const
{
  return m_isValid;
}

iReal Music::getObjectType() const
{
  return Data::MUSIC;
}

bool Music::kill()
{
  if(m_music)
  {
    Mix_FreeMusic(m_music);
    m_music = NULL;
  }
  m_isValid = false;
}

Mix_Music* Music::getMusic() const
{
  return m_music;
}



/* SoundEffect implemenation */

SoundEffect::SoundEffect(string file)
{
  m_effect = Mix_LoadWAV(file.c_str());

  if(m_effect != NULL)
    m_isValid = true;
  else
  {
    m_isValid = false;
    cout << "Mix_LoadWav error: " << Mix_GetError() << endl;
    Utils::log("SoundEffect::SoundEffect error loading sound effect.");
  }
}

SoundEffect::~SoundEffect()
{
}

bool SoundEffect::isValid() const
{
  return m_isValid;
}

iReal SoundEffect::getObjectType() const
{
  return Data::SOUND_EFFECT;
}

bool SoundEffect::kill()
{
  if(m_effect)
  {
    Mix_FreeChunk(m_effect);
    m_effect = NULL;
  }
  m_isValid = false;
}

Mix_Chunk* SoundEffect::getEffect() const
{
  return m_effect;
}
